--- id: TASK-004 title: Config & persistence layer status: "\U0001F3C1 Done" assignee: - '@humdrum-tiv' created_date: '2026-06-16 18:03' updated_date: '2026-06-18 01:17' labels: - feature dependencies: [] priority: high ordinal: 4000 --- ## Description XDG config dir (~/.config/pts/). Persists selected leagues, favorite teams, and active theme. Prereq for league-selection, favorites, and theme-switch tasks. ## Acceptance Criteria - [x] #1 config read/written under XDG config dir - [x] #2 missing/corrupt config falls back to defaults ## Implementation Plan 1. New package internal/config: - Config{Favorites []FavTeam json:favorites; Leagues []string json:leagues,omitempty; Theme string json:theme,omitempty} - FavTeam{League, ID, Abbr, Name string} - dir(): XDG_CONFIG_HOME or ~/.config, then /pts ; path(): dir/config.json - Load() (Config,error): missing file -> zero Config + nil; corrupt JSON -> zero Config + nil (defaults, swallow) [AC#2] - Save(Config) error: mkdir -p dir, marshal indent, atomic write (tmp+rename) [AC#1] 2. Tests (config_test.go, offline, XDG_CONFIG_HOME=t.TempDir): missing->defaults; round-trip Save/Load; corrupt->defaults. ## Final Summary Added internal/config: XDG-based persistence for user prefs. - Config{Favorites, Leagues, Theme} stored as JSON at $XDG_CONFIG_HOME/pts/config.json (falls back to ~/.config/pts/) [AC#1]. - Load() is forgiving: missing or corrupt file returns the zero Config (defaults), never an error, so the app always starts [AC#2]. - Save() writes atomically (temp file + rename), creating the dir as needed. - Leagues/Theme fields reserved for TASK-002/TASK-005; only Favorites wired so far (TASK-003). Tests: config_test.go (offline, XDG_CONFIG_HOME=t.TempDir) covers missing->defaults, Save/Load round-trip, corrupt->defaults.